//
//  CalculatorBrain.swift
//  CalculatorDemo
//
//  Created by 一川 on 4/3/15.
//  Copyright (c) 2015 teng. All rights reserved.
//

import Foundation

class CalculatorBrain{
    
    private var opStack = Array<Op>()//保存表达式
    
    private enum Op {
        case Operand(Double)
        case UnaryOperation(String, Double -> Double)
        case BinaryOperation(String, (Double,Double) -> Double)
        
        var description: String {
            get {
                switch self {
                case .Operand(let operand):
                    return "\(operand)"
                case .UnaryOperation(let symbol, _):
                    return symbol
                case .BinaryOperation(let symbol, _):
                    return symbol
                }
            }
        }
    }
    
    private var knowOps = Dictionary<String,Op>()

    init () {
        func learnOp(op:Op){
            knowOps[op.description] = op
        }
        learnOp(Op.BinaryOperation("+", + ))
        learnOp(Op.BinaryOperation("×", * ))
        learnOp(Op.BinaryOperation("−", { $1 - $0 } ))
        learnOp(Op.BinaryOperation("÷", { $1 / $0 } ))
        learnOp(Op.UnaryOperation("√", sqrt ))
    }
    
    func evaluate() -> Double? {//调用performOperation()完成运算
        changeToPolish(opStack)
        let result = performOperation(opStack)
        return result
    }
    
    func pushOperation(operand:String) {//将输入push到stack中
        if let operation = NSNumberFormatter().numberFromString(operand) {
            opStack.append(Op.Operand(operation.doubleValue))
        } else {
            if let operation = knowOps[operand] {
                opStack.append(operation)
            } else {
                print("need a new operation", appendNewline: false)
            }
        }
    }
    
    private func performOperation(ops:[Op]) -> Double? {//遍历opStack执行运算
        var results = [Double]()
        let remainingOps = ops
        for op in remainingOps {
            switch op {
            case .Operand(let operand):
                results.append(operand)
            case .UnaryOperation(_, let operation):
                if results.isEmpty {
                    return nil
                } else {
                    let num = operation(results.removeLast())
                    results.append(num)
                }
            case .BinaryOperation(_, let operation):
                if results.count < 2 {
                    return nil
                } else {
                    let num = results.removeLast()
                    results.append(operation(num,results.removeLast()))
                }
            }
        }
        opStack.removeAll(keepCapacity: true)
        opStack.append(Op.Operand(results.last!))
        return results.removeLast()
    }
    
    private func changeToPolish(ops:[Op]) {
        var reversePolish = Array<Op>()
        var temp = Array<Op>()
        let remainingOps = ops
        
        for op in remainingOps {//将ops中缀表达式转为后缀表达式存入reversePolish
            switch op {
            case .Operand(let operand):
                reversePolish.append(op)
            default:
                if (temp.isEmpty || op.description == "×" || op.description == "÷") {
                    temp.append(op)
                } else {
                    while (temp.isEmpty || temp.last!.description == "+" || temp.last!.description == "−") {
                        reversePolish.append(temp.removeLast())
                    }
                    temp.append(op)
                }
            }
        }
        while !temp.isEmpty {
            reversePolish.append(temp.removeLast())
        }
        
        opStack = reversePolish
    }
    
    func clear() {
        opStack.removeAll(keepCapacity: true)
    }
}


